package wahadlo.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import wahadlo.model.Model;
import wahadlo.model.ModelRecipient;
import wahadlo.model.Point;

/**
 * Komponent odpowiedzialny za rysowanie wykresu.
 * 
 * @author Koziołek
 * 
 */
public class AxisComponent extends JComponent implements ModelRecipient {
	private static final long serialVersionUID = 1L;
	
	private final Color bkg = Color.WHITE;
	private final Color axis = Color.BLACK;
	private Color funkc = Color.RED;
	
	private final Dimension size = new Dimension(600, 400);
	private Graphics2D canvas;
	private Model model;
	private BufferedImage bufferedImage = null;


	private Point old = null;

	/**
	 * Zbiór kierunków w którym nogą wskazywac strzałki.
	 * 
	 * @author Koziołek
	 * 
	 */
	private enum Direction {
		NORTH, SOUTH, WEST, EAST;
	}

	public AxisComponent() {
		setMinimumSize(size);
	}

	@Override
	public void paint(Graphics g) {
		if (model == null) {
			drawAxis();
		} else {
			drawModel();
		}
		g.drawImage(bufferedImage, 0, 0, null);
	}

	/**
	 * Rysuje osie.
	 */
	private void drawAxis() {
		bufferedImage = new BufferedImage(600, 400,
				BufferedImage.TYPE_3BYTE_BGR);
		canvas = bufferedImage.createGraphics();
		Dimension size = getSize();
		canvas.setColor(bkg);
		canvas.fillRect(0, 0, size.width, size.height);
		canvas.setColor(axis);
		// oś OY
		canvas.drawLine(20, 10, 20, size.height - 10);
		drawArrow(20, 10, Direction.NORTH);
		// oś OX
		canvas.drawLine(15, size.height / 2, size.width - 30,
				size.height / 2);
		drawArrow(size.width - 30, size.height / 2, Direction.WEST);
	}

	private void drawModel() {
		Point point = translate((Point) model.getProperties().get("point"));

		canvas.setColor(funkc);
		int startX;
		int startY;
		int endX = point.getX().intValue();
		int endY = point.getY().intValue();

		if (old != null) {
			startX = old.getX().intValue();
			startY = old.getY().intValue();
		} else {
			startX = point.getX().intValue();
			startY = point.getY().intValue();
		}

		if (this.old != null) {
			canvas.drawLine(startX, startY, endX, endY);
			this.old = point;
		} else {
			canvas.drawLine(startX, startY, endX, endY);
			this.old = point;
		}
	}

	/**
	 * Metoda przesuwa punkt do początku układu współrzędnych
	 * 
	 * @param point
	 *            punkt
	 * @return punkt po przesunięciu
	 */
	private Point translate(Point point) {
		Double translation_scale = (Double) model.getProperties().get(
				"translation_scale");
		if (translation_scale == null)
			translation_scale = 0.;

		double yScale = translation_scale * point.getY() + (size.height) / 2;
		Point translatedPoint = new Point(point.getX() + 20, yScale);
		return translatedPoint;
	}

	/**
	 * Metoda rysuje strzałki.
	 * 
	 * @param x
	 *            wieszchołek strzałki współrzedna x
	 * @param y
	 *            wieszchołek strzałki współrzedna y
	 * @param dir
	 *            kierunek który wskazuje strzałka
	 */
	private void drawArrow(int x, int y, Direction dir) {
		switch (dir) {
		case NORTH:
			canvas.drawLine(x, y, x - 5, y + 10);
			canvas.drawLine(x, y, x + 5, y + 10);
			break;
		case SOUTH:
			canvas.drawLine(x, y, x - 5, y - 10);
			canvas.drawLine(x, y, x + 5, y - 10);
			break;
		case WEST:
			canvas.drawLine(x, y, x - 10, y - 5);
			canvas.drawLine(x, y, x - 10, y + 5);
			break;
		case EAST:
			canvas.drawLine(x, y, x + 10, y - 5);
			canvas.drawLine(x, y, x + 10, y + 5);
			break;
		default:
			break;
		}
	}

	@Override
	public Dimension getMaximumSize() {
		return size;
	}

	@Override
	public Dimension getMinimumSize() {
		return size;
	}

	@Override
	public Dimension getPreferredSize() {
		return size;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see wahadlo.model.ModelRecipient#setModel(wahadlo.model.Model)
	 */
	public void setModel(Model model) {
		this.model = model;
		update();
	}

	/**
	 * metoda aktualizuje stan obiektu po otrzymaniu nowego modelu.
	 */
	private void update() {
		repaint();
	}

	/**
	 * Metoda usuwa dane z obiektu i przywraca jego pierwotny kształt.
	 */
	public void clean() {
		model = null;
		old = null;
		drawAxis();
		update();
	}
}
